package com.ezcoding.engine;

import com.ezcoding.dao.*;
import com.ezcoding.model.*;
import com.ezcoding.utils.StatusUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public abstract class BaseDingdanCalculator implements DingdanCalculator {

    private static final Object lock = new Object();

    Dingdan dingdan;
    private List<DingdanDetail> details;
    private Company company;
    private UserRelation relation;
    private List<Product> products;

    public BaseDingdanCalculator(Dingdan dingdan, List<DingdanDetail> details) {
        this.dingdan = dingdan;
        this.details = details;
    }

    public double getTaxRate() {
        if (dingdan.getIsTicket() == 1) {
            //不开票
            return 1;
        }

        if (getCompany() == null) {
            return 1;
        }

        double taxRate = 1;

        try {
            taxRate = Double.parseDouble(getCompany().getTaxRate());
            taxRate = 1 + taxRate / 100;
        } catch (NumberFormatException ex) {
            ex.printStackTrace();
        }

        return taxRate;
    }

    @Override
    public List<DingdanDetail> getDetails() {
        return details;
    }

    @Override
    public List<Product> getProducts() {
        return products;
    }

    @Override
    public Company getCompany() {
        if (company == null) {
            synchronized (lock) {
                if (company == null) {
                    company = CompanyDao.dao.getCompanyByCompanyId(dingdan.getSourceCompanyId());
                }
            }
        }
        return company;
    }

    public Dingdan getRelatedDingdan() {
        return DingdanDao.dao.getDingdan(dingdan.getTargetUsername(), dingdan.getRelatedDingdanId());
    }

    public boolean deleteRelatedMessage(Dingdan relatedDingdan) {
        relatedDingdan.setState(Dingdan.DINGDAN_STATE_FINISHED);

        Message message = MessageDao.dao.getMessage(relatedDingdan.getSourceUsername(), relatedDingdan.getDingdanId());

        return message == null || message.delete();
    }

    public abstract int getRelationType();

    public abstract double getTotalMoney();

    @Override
    public UserRelation getUserRelation() {
        if (relation == null) {
            synchronized (lock) {
                if (relation == null) {
                    relation = UserRelationDao.dao.getUserRelation(dingdan.getSourceUsername(), dingdan.getTargetUsername(), getRelationType());
                }
            }
        }
        return relation;
    }

    @Override
    public Map<String, Object> checkDingdanDetails() {
        products = new ArrayList<>();
        for (int i = 0; i < details.size(); i++) {
            Map<String, Object> res = checkDingdanDetail(i, details.get(i));
            if (!StatusUtil.getStatusValue(res)) {
                return res;
            }
        }

        return StatusUtil.getSuccessStatusWithEmptyMessage();
    }

    /**
     * 更新商品库存
     *
     * @param product   商品
     * @param needCount 需求数
     */
    public abstract void updateProductStore(Product product, int needCount);

    public abstract void updateProductAndDetail(Product product, DingdanDetail detail);

    public abstract void updateProfit(DingdanDetail detail, double inPrice);

    public abstract void updateProductInPrice(Product product, DingdanDetail detail);

    @Override
    public Map<String, Object> updateUserRelation() {
        UserRelation relation = getUserRelation();

        if (relation == null) {
            return StatusUtil.getFailedStatusWithMessage("用户关系不存在！");
        }

        relation.setTicketIn(relation.getTicketIn() + dingdan.getTicketIn());
        relation.setTicketOut(relation.getTicketOut() + dingdan.getTicketOut());
        relation.setMoneyIn(relation.getMoneyIn() + dingdan.getMoneyIn());
        relation.setMoneyOut(relation.getMoneyOut() + dingdan.getMoneyOut());

        if (!relation.update()) {
            return StatusUtil.getFailedStatusWithMessage("用户关系更新失败！");
        }

        return StatusUtil.getSuccessStatusWithEmptyMessage();
    }

    private Map<String, Object> checkDingdanDetail(int index, DingdanDetail detail) {
        Product product = ProductDao.dao.getProductByCompanyIdAndProductId(dingdan.getSourceCompanyId(), detail.getProductId());

        if (product == null) {
            return StatusUtil.getFailedStatusWithMessage(String.format("商品不存在[%d]", index + 1));
        }

        if (detail.getCount() <= 0) {
            return StatusUtil.getFailedStatusWithMessage(String.format("商品数量必须为正数[%d, %s]", index + 1, product.getName()));
        }

        if (detail.getPrice() < 0) {
            return StatusUtil.getFailedStatusWithMessage(String.format("商品单价必须为非负数[%d, %s]", index + 1, product.getName()));
        }

        if (!checkProductStore(product.getCount(), detail.getCount())) {
            return StatusUtil.getFailedStatusWithMessage(String.format("商品库存不足[%d, %s]", index + 1, product.getName()));
        }

        detail.setId(null);
        products.add(product);

        return StatusUtil.getSuccessStatusWithEmptyMessage();
    }

    /**
     * 检查商品库存
     *
     * @param curCount  当前库存数量
     * @param needCount 请求数量
     * @return true：库存正常，满足销售或采退数量
     */
    public abstract boolean checkProductStore(int curCount, int needCount);

    @Override
    public void calDingdanInfo() {
        double curTotalTicket = dingdan.getFreeTicket() + dingdan.getTicket();
        double curTotalMoney = getTotalMoney();
        double totalTicket = 0;
        double totalMoney = 0;
        int dingdanTotalCount = 0;
        double dingdanTotalMoney = 0;
        double totalProfit = 0;
        for (int i = 0; i < details.size(); i++) {
            Product product = products.get(i);
            DingdanDetail detail = details.get(i);
            double curValue = detail.getCount() * detail.getPrice();

            double curTicket = getCurValue(curValue, curTotalTicket);
            curTicket = getCurTicket(curTicket);
            curTotalTicket = getCurTotalValue(curValue, curTotalTicket);

            double curMoney = getCurValue(curValue, curTotalMoney);
            curMoney = getCurMoney(curMoney);
            curTotalMoney = getCurTotalValue(curValue, curTotalMoney);

            setDetailTicket(detail, curTicket);
            setDetailMoney(detail, curMoney);
            totalTicket += curTicket;
            totalMoney += curMoney;
            dingdanTotalCount += detail.getCount();
            dingdanTotalMoney += detail.getCount() * detail.getPrice();

            updateProductAndDetail(product, detail);
            totalProfit += detail.getProfit();
        }

        setTicket(totalTicket);
        setMoney(totalMoney);
        dingdan.setTotalCount(dingdanTotalCount);
        dingdan.setTotalMoney(dingdanTotalMoney);
        dingdan.setProfit(totalProfit);
    }

    public abstract double getCurTicket(double curValue);

    public abstract double getCurMoney(double curValue);

    public abstract void setDetailTicket(DingdanDetail detail, double value);

    public abstract void setDetailMoney(DingdanDetail detail, double value);

    public abstract void setTicket(double value);

    public abstract void setMoney(double value);

    private double getCurValue(double curValue, double curTotalValue) {
        if (curTotalValue == 0) {
            return curValue;
        }

        if (curTotalValue < 0) {
            return curValue - curTotalValue;
        }

        if (curValue <= curTotalValue) {
            return 0;
        }

        return curValue - curTotalValue;
    }

    private double getCurTotalValue(double curValue, double curTotalValue) {
        if (curValue <= curTotalValue) {
            return curTotalValue - curValue;
        }

        return 0;
    }
}
